home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Merciful 2
/
Merciful - Disc 2.iso
/
software
/
d
/
devioustools25.dms
/
devioustools25.adf
/
utils
/
003.lzx
/
AMountains
/
artist.c
< prev
next >
Wrap
C/C++ Source or Header
|
2004-02-13
|
20KB
|
658 lines
/************************************************************************/
/* routines to render a fractal landscape as an image */
/************************************************************************/
#include <proto/dos.h>
#include <m68881.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "paint.h"
#include "crinkle.h"
#include "global.h"
static Col get_col( Height, Height, Height, Height );
static int project( int, Height );
#define SIDE 1.0
double vstrength; // strength of vertical light source
double lstrength; // strength of vertical light source
int base; // parity flag for mirror routine
Parm fold_param;
/* -------------------------------------------------------------------- */
/* setup the colour lookup table */
/* -------------------------------------------------------------------- */
void set_clut( int max_col, Gun *red, Gun *green, Gun *blue )
{
int band, shade;
double top, bot;
double intensity;
int tmp;
/* ---------------------------------------------------------------- */
/* double rb[N_BANDS] = { 0.167,0.200,0.333,0.450,0.600,1.000 }; */
/* double gb[N_BANDS] = { 0.667,0.667,0.500,0.500,0.600,1.000 }; */
/* double bb[N_BANDS] = { 0.500,0.450,0.333,0.200,0.000,1.000 }; */
/* ---------------------------------------------------------------- */
double rb[N_BANDS];
double gb[N_BANDS];
double bb[N_BANDS];
/* ---------------------------------------------------------------- */
/* band base colours as RGB fractions */
/* ---------------------------------------------------------------- */
rb[0] = 0.450; rb[1] = 0.600; rb[2] = 1.000;
gb[0] = 0.500; gb[1] = 0.600; gb[2] = 1.000;
bb[0] = 0.333; bb[1] = 0.000; bb[2] = 1.000;
red[BLACK] = 0; // black
green[BLACK] = 0;
blue[BLACK] = 0;
red[WHITE] = COL_RANGE; // white
green[WHITE] = COL_RANGE;
blue[WHITE] = COL_RANGE;
red[SKY] = 0.404 * COL_RANGE; // sky
green[SKY] = 0.588 * COL_RANGE;
blue[SKY] = COL_RANGE;
red[SEA_LIT] = 0; // sea (lit)
green[SEA_LIT] = 0.500 * COL_RANGE;
blue[SEA_LIT] = 0.700 * COL_RANGE;
red[SEA_UNLIT] = 0; // sea (unlit)
green[SEA_UNLIT] = ((ambient+(vfract/(1.0+vfract)))*0.500)*COL_RANGE;
blue[SEA_UNLIT] = ((ambient+(vfract/(1.0+vfract)))*0.700)*COL_RANGE;
if( MIN_COL > max_col ) {
PutStr( "set_clut: less than the minimum number of colours available\n" );
exit( 1 );
}
/* ---------------------------------------------------------------- */
/* max_col can over-rule band_size */
/* ---------------------------------------------------------------- */
while( BAND_BASE + band_size * N_BANDS > max_col ) {
band_size--;
}
for ( band = 0; band<N_BANDS; band++ ) {
for( shade = 0; shade < band_size; shade++ ) {
if( BAND_BASE + band * band_size + shade >= max_col ) {
PutStr( "INTERNAL ERROR, overflowed clut\n" );
exit( 1 );
}
/* -------------------------------------------------------- */
/* set red */
/* -------------------------------------------------------- */
top = rb[band];
bot = ambient * top;
intensity = bot + ( shade * ( top - bot ) ) / ( band_size - 1 );
tmp = COL_RANGE * intensity;
if ( tmp < 0 ) {
Printf( "set_clut: internal error: invalid code %ld\n", tmp );
exit( 2 );
}
if( tmp > COL_RANGE ) {
tmp = COL_RANGE;
}
red[BAND_BASE + band * band_size + shade] = tmp;
/* -------------------------------------------------------- */
/* set green */
/* -------------------------------------------------------- */
top = gb[band];
bot = ambient * top;
intensity = bot + ( shade * ( top - bot ) ) / ( band_size - 1 );
tmp = COL_RANGE * intensity;
if ( tmp < 0 ) {
Printf( "set_clut: internal error: invalid code %ld\n", tmp );
exit( 2 );
}
if( tmp > COL_RANGE ) {
tmp = COL_RANGE;
}
green[BAND_BASE + band * band_size + shade] = tmp;
/* -------------------------------------------------------- */
/* set blue */
/* -------------------------------------------------------- */
top = bb[band];
bot = ambient * top;
intensity = bot + shade * ( top - bot ) / ( band_size - 1 );
tmp = COL_RANGE * intensity;
if ( tmp < 0 ) {
Printf( "set_clut: internal error: invalid code %ld\n", tmp );
exit( 2 );
}
if( tmp > COL_RANGE ) {
tmp = COL_RANGE;
}
blue[BAND_BASE + band * band_size + shade] = tmp;
}
}
}
/* -------------------------------------------------------------------- */
/* extract the table of heights from the Strip struct and discard the */
/* rest of the struct. */
/* -------------------------------------------------------------------- */
Height *extract( Strip *s )
{
int i;
Height *p;
p = s->d;
free( s );
for( i = 0; i < width; i++ ) {
p[i] = shift + vscale * p[i];
}
return p;
}
/* -------------------------------------------------------------------- */
/* initialise the variables for the artist routines. */
/* -------------------------------------------------------------------- */
void init_artist_variables( void )
{
double dh, dd;
int pwidth; // longest lengthscale for update
width = ( 1 << levels ) + 1;
pwidth = ( 1 << ( levels - stop ) ) + 1;
/* ---------------------------------------------------------------- */
/* make the fractal SIDE wide, this makes it easy to predict the */
/* average height returned by calcalt. If we have stop != 0 then */
/* make the largest update length = SIDE */
/* ---------------------------------------------------------------- */
cos_phi = cos( phi );
sin_phi = sin( phi );
tan_phi = tan( phi );
x_fact = cos_phi * cos( alpha );
y_fact = cos_phi * sin( alpha );
vscale = stretch * pwidth; // have approx same height as fractal width
// this makes each pixel SIDE=1.0 wide.
// c.f. get_col
delta_shadow = tan_phi / cos( alpha );
shadow_slip = tan( alpha );
/* ---------------------------------------------------------------- */
/* guess the average height of the fractal */
/* ---------------------------------------------------------------- */
varience = vscale * pow( SIDE , 2.0 * fdim );
shift *= varience;
varience += shift;
start = ( sealevel - shift ) / vscale; // always start at sealevel
/* ---------------------------------------------------------------- */
/* set the position of the view point */
/* ---------------------------------------------------------------- */
viewheight = altitude * width;
viewpos = - distance * width;
/* ---------------------------------------------------------------- */
/* set viewing angle and focal length (vertical-magnification) */
/* try mapping the bottom of the fractal to the bottom of the */
/* screen. Try to get points in the middle of the fractal */
/* to be 1 pixel high */
/* ---------------------------------------------------------------- */
dh = viewheight;
dd = width / 2.0 - viewpos;
focal = sqrt( dd * dd + dh * dh );
#ifndef SLOPPY
tan_vangle = ( sealevel - viewheight ) / viewpos;
vangle = atan ( tan_vangle ) - atan( height / focal / 2.0 );
#else
/* ---------------------------------------------------------------- */
/* we are making some horrible approximations to avoid trig funcs */
/* ---------------------------------------------------------------- */
tan_vangle = ( sealevel - viewheight ) / viewpos - height / 2.0 / focal;
#endif
fold_param.mean = mean;
fold_param.rg1 = smooth & 4;
fold_param.rg2 = smooth & 2;
fold_param.rg3 = smooth & 1;
fold_param.cross = cross;
fold_param.force_front = slope;
fold_param.force_back = 0;
fold_param.forceval = forceheight;
fold_param.mix = mix;
fold_param.midmix = midmix;
fold_param.fdim = fdim;
top = make_fold( &fold_param, levels, stop, SIDE / pwidth );
/* ---------------------------------------------------------------- */
/* use first set of heights to set shadow value */
/* ---------------------------------------------------------------- */
shadow = extract( next_strip( top ) );
a_strip = extract( next_strip( top ) );
b_strip = extract( next_strip( top ) );
/* ---------------------------------------------------------------- */
/* initialise the light strengths */
/* ---------------------------------------------------------------- */
vstrength = vfract * contrast / ( 1.0 + vfract );
lstrength = contrast / ( 1.0 + vfract );
}
/* -------------------------------------------------------------------- */
/* calculate the colour of a point. */
/* -------------------------------------------------------------------- */
static Col get_col( Height p, Height p_minus_x, Height p_minus_y, Height shadow )
{
Height delta_x,
delta_y,
delta_x_sqr,
delta_y_sqr,
hypot_sqr;
double norm, dshade;
Height effective;
Col result;
int band, shade;
/* ---------------------------------------------------------------- */
/* if underwater */
/* ---------------------------------------------------------------- */
if ( p < sealevel ) {
if( shadow > sealevel ) {
return SEA_UNLIT;
}
else {
return SEA_LIT;
}
}
/* ---------------------------------------------------------------- */
/* We have three light sources, one slanting in from the left one */
/* directly from above and an ambient light. */
/* For the directional sources illumination is proportional to the */
/* cosine between the normal to the surface and the light. */
/* */
/* The surface contains two vectors */
/* ( 1, 0, delta_x ) */
/* ( 0, 1, delta_y ) */
/* */
/* The normal therefore is parallel to */
/* ( -delta_x, -delta_y, 1 ) / sqrt( 1 + delta_x² + delta_y² ) */
/* */
/* For light parallel to ( cos_phi, 0, -sin_phi ) the cosine is */
/* ( cos_phi * delta_x + sin_phi ) / sqrt(1 + delta_x² + delta_y²) */
/* */
/* For light parallel to */
/* ( cos_phi * cos_alpha, cos_phi * sin_alpha, -sin_phi ) */
/* the cosine is */
/* cos_phi * cos_alpha * delta_x + cos_phi * sin_alpha * delta_y + sin_phi */
/* ----------------------------------------------------------------------- */
/* sqrt( 1 + delta_x² + delta_y² ) */
/* */
/* For vertical light the cosine is */
/* 1 / sqrt( 1 + delta_x² + delta_y² ) */
/* ---------------------------------------------------------------- */
delta_x = p - p_minus_x;
delta_y = p - p_minus_y;
delta_x_sqr = delta_x * delta_x;
delta_y_sqr = delta_y * delta_y;
hypot_sqr = delta_x_sqr + delta_y_sqr;
norm = sqrt( 1.0 + hypot_sqr );
/* ---------------------------------------------------------------- */
/* calculate effective height */
/* ---------------------------------------------------------------- */
effective = p + varience * contour / ( 1.0 + hypot_sqr );
/* ---------------------------------------------------------------- */
/* calculate colour band. */
/* ---------------------------------------------------------------- */
band = ( effective / varience ) * N_BANDS;
if ( band < 0 ) {
band = 0;
}
if( band > N_BANDS - 1 ) {
band = N_BANDS - 1;
}
result = BAND_BASE + ( band * band_size );
/* ---------------------------------------------------------------- */
/* calculate the illumination stength */
/* */
/* add in a contribution for the vertical light. The normalisation */
/* factor is applied later */
/* ---------------------------------------------------------------- */
dshade = vstrength;
if( p >= shadow ) {
/* ------------------------------------------------------------ */
/* add in contribution from the main light source */
/* ------------------------------------------------------------ */
dshade += lstrength * ( delta_x * x_fact + delta_y * y_fact + sin_phi );
}
/* ---------------------------------------------------------------- */
/* divide by the normalisation factor (the same for both light */
/* sources) */
/* ---------------------------------------------------------------- */
dshade /= norm;
/* ---------------------------------------------------------------- */
/* calculate shading */
/* */
/* dshade should be in the range 0.0 -> 1.0 */
/* if the light intensities add to 1.0 */
/* now convert to an integer */
/* ---------------------------------------------------------------- */
shade = dshade * band_size;
if ( shade > band_size - 1 ) {
shade = band_size - 1;
}
/* ---------------------------------------------------------------- */
/* if shade is negative then point is really in deep shadow */
/* ---------------------------------------------------------------- */
if( shade < 0 ) {
shade = 0;
}
result += shade;
if( result >= n_col || result < 0 ) {
Printf( "INTERNAL ERROR colour out of range %ld\n", result );
exit( 1 );
}
return result;
}
/* -------------------------------------------------------------------- */
/* This routine returns a plan view of the surface */
/* -------------------------------------------------------------------- */
Col *makemap( Height *a, Height *b, Height *shadow )
{
Col *res;
int i;
res = (Col *) malloc( width * sizeof( Col ) );
if ( res == NULL ) {
PutStr( "malloc failed for colour strip\n" );
exit( 1 );
}
res[0] = BLACK;
for( i = 1; i < width; i++ ) {
res[i] = get_col( b[i], a[i], b[i-1], shadow[i] );
}
return res;
}
/* -------------------------------------------------------------------- */
/* this routine returns a perspective view of the surface */
/* -------------------------------------------------------------------- */
Col *camera( Height *a, Height *b, Height *shadow )
{
int i, coord, last;
Col *res, col;
res = (Col *) malloc( height * sizeof( Col ) );
if( res == NULL ) {
PutStr( "malloc failed for picture strip\n" );
exit( 1 );
}
/* ---------------------------------------------------------------- */
/* optimised painters algorithm */
/* */
/* scan from front to back, we can avoid calculating the */
/* colour if the point is not visable. */
/* ---------------------------------------------------------------- */
for( i = last = 0 ; i < width && last < height; i++ ) {
if( a[i] < sealevel ) {
a[i] = sealevel;
}
coord = 1 + project( i, a[i] );
if ( coord > last ) {
/* -------------------------------------------------------- */
/* get the colour of this point, the front strip should be */
/* black */
/* -------------------------------------------------------- */
if ( i == 0 ) {
col = BLACK;
}
else {
col = get_col( b[i], a[i], b[i-1], shadow[i] );
}
if ( coord > height ) {
coord = height;
}
for ( ; last<coord; last++ ) {
res[last] = col;
}
}
}
for ( ; last < height; last++ ) {
res[last] = SKY;
}
return res;
}
/* -------------------------------------------------------------------- */
/* this routine returns a perspective view of the surface with */
/* reflections in the water */
/* -------------------------------------------------------------------- */
Col *mirror( Height *a, Height *b, Height *shadow )
{
Col *res, *map;
Col last_col;
int i, j, top, bottom, coord;
int last_top, last_bottom;
Height pivot;
res = (Col *) malloc( height * sizeof( Col ) );
if( res == NULL ) {
PutStr( "malloc failed for picture strip\n" );
exit( 1 );
}
last_col = SKY;
last_top = height - 1;
last_bottom = 0;
/* ---------------------------------------------------------------- */
/* many of the optimisation in the camera routine are hard to */
/* implement in this case so we revert to the simple painters */
/* algorithm modified to produce reflections scan from back to */
/* front drawing strips between the projected position of height */
/* and -height. for water stipple the colour so the reflection is */
/* still visable */
/* ---------------------------------------------------------------- */
map = makemap( a, b, shadow );
pivot = 2.0 * sealevel;
for ( i = width - 1; i > 0; i-- ) {
if ( map[i] < BAND_BASE ) {
/* -------------------------------------------------------- */
/* stipple water values */
/* -------------------------------------------------------- */
for( j = last_bottom; j <= last_top; j++ ) {
res[j] = last_col;
}
last_col = map[i];
/* -------------------------------------------------------- */
/* invalidate strip so last stip does not exist */
/* -------------------------------------------------------- */
last_bottom = height;
last_top = -1;
/* -------------------------------------------------------- */
/* fill in water values */
/* -------------------------------------------------------- */
coord = 1 + project( i, sealevel );
for ( j = 0; j < coord; j++ ) {
/* ---------------------------------------------------- */
/* do not print on every other point if the current */
/* value is a land value */
/* ---------------------------------------------------- */
if ( ( j + base ) % 2 || res[j] < BAND_BASE ) {
res[j] = map[i];
}
}
/* -------------------------------------------------------- */
/* skip any adjacent bits of water with the same colour */
/* -------------------------------------------------------- */
while ( map[i] == last_col ) {
i--;
}
i++; // the end of the for loop will decrement as well
}
else {
/* -------------------------------------------------------- */
/* draw land values */
/* -------------------------------------------------------- */
top = project( i, a[i] );
bottom = project( i, pivot - a[i] );
if( last_col == map[i] ) {
if( top > last_top ) {
last_top = top;
}
if( bottom < last_bottom ) {
last_bottom = bottom;
}
}
else {
if ( top < last_top ) {
for ( j = top + 1; j <= last_top; j++ ) {
res[j] = last_col;
}
}
if ( bottom > last_bottom ) {
for ( j = last_bottom; j < bottom; j++ ) {
res[j] = last_col;
}
}
last_top = top;
last_bottom = bottom;
last_col = map[i];
}
}
}
/* ---------------------------------------------------------------- */
/* draw in front face */
/* ---------------------------------------------------------------- */
for ( j = last_bottom; j <= last_top; j++ ) {
res[j] = last_col;
}
if( a[0] < sealevel ) {
coord = 1 + project( 0, sealevel );
}
else {
coord = 1 + project( 0, a[0] );
}
for( j = 0; j < coord; j++ ) {
res[j] = map[0];
}
base = 1 - base;
free( map );
return res;
}
/* -------------------------------------------------------------------- */
/* project a point onto the screen position */
/* -------------------------------------------------------------------- */
static int project( int x, Height y )
{
int pos;
#ifndef SLOPPY
double theta;
theta = atan( ( viewheight - y ) / ( x - viewpos ) ) - vangle;
pos = height / 2 - focal * tan( theta );
#else
double tan_theta;
/* ---------------------------------------------------------------- */
/* nast approx to avoid trig functions */
/* ---------------------------------------------------------------- */
tan_theta = ( viewheight - y ) / ( x - viewpos ) - tan_vangle;
pos = height / 2 - focal * tan_theta;
#endif
if( pos > height - 1 ) {
pos = height - 1;
}
else if ( pos < 0 ) {
pos = 0;
}
return pos;
}
/* -------------------------------------------------------------------- */
/* Tidy up and free everything. */
/* -------------------------------------------------------------------- */
void finish_artist( void )
{
free( a_strip );
free( b_strip );
free( shadow );
free_fold( top );
}